package com.dingup.mock.module;

import com.dingup.mock.bean.exam.TsMockReport;
import com.dingup.mock.util.MockUtil;
import com.dingup.util.GDateUtil;
import com.dingup.util.GOssUtil;
import com.dingup.util.GPinYinUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.Charset;

/**
 * Created by yanggavin on 16/7/18.
 */
public class SMockPdfReportGenerator {
    private static class LazyHolder {
        private static final SMockPdfReportGenerator INSTANCE = new SMockPdfReportGenerator();
    }
    public static SMockPdfReportGenerator getInstance() {
        return SMockPdfReportGenerator.LazyHolder.INSTANCE;
    }
    private SMockPdfReportGenerator() { }

    private Logger logger = Logger.getLogger(SMockPdfReportGenerator.class);
    private final String OSS_URL_PREFIX = "http://onlinetest-public.oss-cn-beijing.aliyuncs.com";
    private final String OSS_BUCKET_NAME = "onlinetest-public";
    private final String OSS_BUCKET_KEY_FOLDER_PART = "mock_report";
    private final String PDF_CONTENT_TYPE = "application/pdf";
    private final String CH_FONT_FILE_PATH = "/font/SimHei.ttf";
    private final String IMG_RELATIVE_PATH = "https://ts-dingup-onlinetest.oss-cn-beijing.aliyuncs.com/img/home/";
    private String reportModelHtml = "";

    /**
     * 载入模考报告模板页面数据
     */
    private void initData(){
        try {
            if (StringUtils.isBlank(reportModelHtml)) {
                InputStream is = this.getClass().getResourceAsStream("/mock_report_model.html");
                reportModelHtml = IOUtils.toString(is, Charset.defaultCharset());
            }
        } catch (Exception e){
            logger.error("载入权限数据异常:" + e.getMessage());
        }
    }

    /**
     * 生成pdf报告,并上传至oss服务器
     * @param tmr
     * @return  报告的oss链接
     */
    public String makePdfReportAndSaveToOss(TsMockReport tmr){
        String retUrl = "";
        try {
            initData();
            // 替换模板页面中的数据部分
            String curReportHtmlContent = fillDataIntoHtml(tmr);
            // 调用pdf生成工具,生成pdf报告,并上传到Oss
            retUrl = createPdfAndSaveToOss(curReportHtmlContent, (tmr.getExamId() + ".pdf"));
        } catch (Exception e){
            logger.error(e.getMessage());
        } finally {
            return retUrl;
        }
    }

    private String fillDataIntoHtml(TsMockReport tmr){
        String finalHtml = reportModelHtml;
        try {
            finalHtml = finalHtml.replace("{{user_name}}", GPinYinUtil.getPingYin(tmr.getUserName()));
            finalHtml = finalHtml.replace("{{test_date}}", GDateUtil.getShortDateStr(tmr.getCreateTime()));
            int totalScore = 0;
            // reading
            String[] rsArr = tmr.getReadingScore().split(",");
            if (rsArr.length == 4) {
                int rcNum = Integer.parseInt(rsArr[0]);
                int rfNum = Integer.parseInt(rsArr[1]);
                int rpNum = Integer.parseInt(rsArr[2]);
                int rtScore = Integer.parseInt(rsArr[3]);
                int rqNum = rcNum + rfNum + rpNum;
                String rcPercent = String.format("%s%%", (rcNum * 100 / rqNum));
                finalHtml = finalHtml.replace("{{reading_correct_num}}", rsArr[0]);
                finalHtml = finalHtml.replace("{{reading_fail_num}}", rsArr[1]);
                finalHtml = finalHtml.replace("{{reading_pass_num}}", rsArr[2]);
                finalHtml = finalHtml.replace("{{reading_total_num}}", rqNum + "");
                finalHtml = finalHtml.replace("{{reading_correct_percent}}", rcPercent + "");
                finalHtml = finalHtml.replace("{{reading_total_score}}", rtScore + "");
                totalScore += rtScore;
                // score detail.
                finalHtml = finalHtml.replace("{{reading_score_evaluate}}", getReadingScoreEvaluate(rtScore));
                finalHtml = finalHtml.replace("{{reading_evaluate_description}}", getReadingEvaluateDes(rtScore));
            }
            // listening
            String[] lsArr = tmr.getListeningScore().split(",");
            if (lsArr.length == 4) {
                int lcNum = Integer.parseInt(lsArr[0]);
                int lfNum = Integer.parseInt(lsArr[1]);
                int lpNum = Integer.parseInt(lsArr[2]);
                int ltScore = Integer.parseInt(lsArr[3]);
                int lqNum = lcNum + lfNum + lpNum;
                String lcPercent = String.format("%s%%", (lcNum * 100 / lqNum));
                finalHtml = finalHtml.replace("{{listening_correct_num}}", lsArr[0]);
                finalHtml = finalHtml.replace("{{listening_fail_num}}", lsArr[1]);
                finalHtml = finalHtml.replace("{{listening_pass_num}}", lsArr[2]);
                finalHtml = finalHtml.replace("{{listening_total_num}}", lqNum + "");
                finalHtml = finalHtml.replace("{{listening_correct_percent}}", lcPercent + "");
                finalHtml = finalHtml.replace("{{listening_total_score}}", lsArr[3]);
                totalScore += ltScore;
                // score detail.
                finalHtml = finalHtml.replace("{{listening_score_evaluate}}", getListeningScoreEvaluate(ltScore));
                finalHtml = finalHtml.replace("{{listening_evaluate_description}}", getListeningEvaluateDes(ltScore));
            }
            // speaking
            String[] ssArr = tmr.getSpeakingScore().split(",");
            if (ssArr.length == 6) {
                int stScore = MockUtil.getSpeakingScore((Integer.parseInt(ssArr[0]) + Integer.parseInt(ssArr[1]) + Integer.parseInt(ssArr[2])
                        + Integer.parseInt(ssArr[3]) + Integer.parseInt(ssArr[4]) + Integer.parseInt(ssArr[5])));
                finalHtml = finalHtml.replace("{{speaking_total_score}}", stScore + "");
                finalHtml = finalHtml.replace("{{speaking_q1_score}}", ssArr[0]);
                finalHtml = finalHtml.replace("{{speaking_q2_score}}", ssArr[1]);
                finalHtml = finalHtml.replace("{{speaking_q3_score}}", ssArr[2]);
                finalHtml = finalHtml.replace("{{speaking_q4_score}}", ssArr[3]);
                finalHtml = finalHtml.replace("{{speaking_q5_score}}", ssArr[4]);
                finalHtml = finalHtml.replace("{{speaking_q6_score}}", ssArr[5]);
                totalScore += stScore;
                // score detail.
                int familyScore = Integer.parseInt(ssArr[0]) + Integer.parseInt(ssArr[1]);
                int campusScore = Integer.parseInt(ssArr[2]) + Integer.parseInt(ssArr[4]);
                int academicScore = Integer.parseInt(ssArr[3]) + Integer.parseInt(ssArr[5]);
                finalHtml = finalHtml.replace("{{speaking_score_evaluate_familiar}}", getSpeakingScoreEvaluate(familyScore));
                finalHtml = finalHtml.replace("{{speaking_evaluate_description_familiar}}", getSpeakingEvaluateDes4FamilySkill(familyScore));
                finalHtml = finalHtml.replace("{{speaking_score_evaluate_campus}}", getSpeakingScoreEvaluate(campusScore));
                finalHtml = finalHtml.replace("{{speaking_evaluate_description_campus}}", getSpeakingEvaluateDes4CampusSkill(campusScore));
                finalHtml = finalHtml.replace("{{speaking_score_evaluate_academic}}", getSpeakingScoreEvaluate(academicScore));
                finalHtml = finalHtml.replace("{{speaking_evaluate_description_academic}}", getSpeakingEvaluateDes4AcademicSkill(academicScore));
            }
            // writing
            String[] wsArr = tmr.getWritingScore().split(",");
            if (wsArr.length == 2) {
                float readScore = Float.parseFloat(wsArr[0]);
                float knowledgeScore = Float.parseFloat(wsArr[1]);
                int wtScore = MockUtil.getWritingScore(readScore + knowledgeScore);
                finalHtml = finalHtml.replace("{{writing_total_score}}", wtScore + "");
                finalHtml = finalHtml.replace("{{writing_q1_score}}", wsArr[0]);
                finalHtml = finalHtml.replace("{{writing_q2_score}}", wsArr[1]);
                totalScore += wtScore;
                // score detail.
                finalHtml = finalHtml.replace("{{writing_score_evaluate_read}}", getWritingScoreEvaluate(readScore));
                finalHtml = finalHtml.replace("{{writing_evaluate_description_read}}", getWritingEvaluateDes4ReadingSkill(readScore));
                finalHtml = finalHtml.replace("{{writing_score_evaluate_knowledge}}", getWritingScoreEvaluate(knowledgeScore));
                finalHtml = finalHtml.replace("{{writing_evaluate_description_knowledge}}", getWritingEvaluateDes4KnowledgeSkill(knowledgeScore));
            }
            // total
            finalHtml = finalHtml.replace("{{all_total_score}}", totalScore + "");
        } catch (Exception e){
            logger.error(e.getMessage());
        } finally {
            return finalHtml;
        }
    }

    /**
     * 将HTML字符串内容转换为pdf并上传到Oss
     * @param htmlContent   html字符串
     * @return  Oss文件地址
     */
    private String createPdfAndSaveToOss(String htmlContent, String fileName) throws Exception {
        String retUrl = "";
        ITextRenderer renderer;
        try {
            renderer = new ITextRenderer();
            renderer.setDocumentFromString(htmlContent);
            // set options.
            // use these code to specify chinese font.
            // ITextFontResolver fontResolver = renderer.getFontResolver();
            // fontResolver.addFont(CH_FONT_FILE_PATH, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            renderer.getSharedContext().setBaseURL(IMG_RELATIVE_PATH);
            // layout & create pdf.
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            renderer.layout();
            renderer.createPDF(baos);

            int fileLength = baos.size();
            // save pdf to oss.
            InputStream is = new ByteArrayInputStream(baos.toByteArray());
            if (is != null) {
                // if exist, then delete it.
                String fileOssUrl = String.format("%s/%s/%s", OSS_URL_PREFIX, OSS_BUCKET_KEY_FOLDER_PART, fileName);
                GOssUtil.deleteObjectInOss(fileOssUrl);
                // 上传文件到Oss
                retUrl = GOssUtil.putObjectToOss(is, fileLength,
                        PDF_CONTENT_TYPE, OSS_BUCKET_KEY_FOLDER_PART, fileName, OSS_BUCKET_NAME);
                // 替换为https链接
                retUrl = GOssUtil.replaceHttpToHttps(retUrl);
                logger.info("New pdf oss url:" + retUrl);
            }
            // close.
            is.close();
            baos.close();
            renderer.finishPDF();
        } catch (Exception e){
            throw e;
        } finally {
            return retUrl;
        }
    }

    private String getReadingScoreEvaluate(int score){
        String ret = "";
        if (score <= 14){
            ret = "LOW(0-14)";
        } else if (score > 14 && score <=21){
            ret = "INTERMEDIATE(15-21)";
        } else if (score > 21){
            ret = "HIGH(22-30)";
        }
        return ret;
    }

    private String getReadingEvaluateDes(int score){
        String retDes = "";
        if (score <= 14){
            retDes = "<p>Test takers who receive a score at the <b>LOW</b> level typically understand some of the information presented in academic texts in English that require a wide range of reading abilities,but their understanding is limited.</p><p>Test takers who receive a score at the<b>LOW</b>level typically:</p><ul><li>have a command of basic academic vocabulary,but their understanding of less common vocabulary is inconsistent</li><li>have limited ability to understand and connect information,have di culty recognizing paraphrases of text information,and often rely on particular words and phrases rather than a complete understanding of the text</li><li>have di culty identifying the author’s purpose,except when that purpose is explicitly stated in the text or easy to infer from the text</li><li>can sometimes recognize major ideas from a text when the information is clearly presented,memorable or illustrated by examples,but have di culty doing so when the text is more demanding</li></ul>";
        } else if (score > 14 && score <=21){
            retDes = "<p>Test takers who receive a score at the <b>INTERMEDIATE</b> level typically understand academic texts in English that require a wide range of reading abilities,although their understanding of certain parts of the texts is limited.</p><p>Test takers who receive a score at the<b>INTERMEDIATE</b>level typically:</p><ul><li>have a good command of common academic vocabulary,but still have some di culty with high-level vocabulary</li><li>have a very good understanding of grammatical structure</li><li>can understand and connect information,make appropriate inferences,and synthesize information in a range of texts,but have more di culty when the vocabulary is high level and the text is conceptually dense</li><li>can recognize the expository organization of a text and the role that speci c information serves within a larger text,but have some di culty when these are not explicit or easy to infer from the text</li><li>can abstract major ideas from a text,but have more di culty doing so when the text is conceptually dense</li></ul>";
        } else if (score > 21){
            retDes = "<p>Test takers who receive a score at the <b>HIGH</b> level typically understand academic texts in English that require a wide range of reading abilities regardless of the difficulty of the texts.</p><p>Test takers who score at the<b>HIGH</b>level typically:</p><ul><li>have a very good command of academic vocabulary and grammatical structure</li><li>can understand and connect information,make appropriate inferences and synthesize ideas,even when the text is conceptually dense and the language is complex</li><li>can recognize the expository organization of a text and the role that specific information serves within the larger text,even when the text is conceptually dense</li><li>can abstract major ideas from a text,even when the text is conceptually dense and contains complex language</li></ul>";
        }
        return retDes;
    }

    private String getListeningScoreEvaluate(int score){
        String ret = "";
        if (score <= 13){
            ret = "LOW(0-13)";
        } else if (score > 13 && score <=21){
            ret = "INTERMEDIATE(14-21)";
        } else if (score > 21){
            ret = "HIGH(22-30)";
        }
        return ret;
    }

    private String getListeningEvaluateDes(int score){
        String retDes = "";
        if (score <= 13){
            retDes = "<p>Test takers who receive a score at the <b>LOW</b> level typically understand the main idea and some important details of conversations.However,test takers at the low level may have di culty understanding lectures and conversations in English that involve abstract or complex ideas and recognizing the relationship between those ideas.Test takers at this level also may not understand sections of lectures and conversations that contain di cult vocabulary or complex grammatical structures.</p><p>Test takers at the<b>LOW</b>level typically can:</p><ul><li>understand main ideas when they are stated explicitly or marked as important,but may have di culty understanding main ideas if they are not stated explicitly</li><li>understand important details when they are stated explicitly or marked as important,but may have di culty understanding details if they are not repeated or clearly marked as important,or if they are conveyed over several exchanges among di erent speakers</li><li>understand ways that speakers use language to emphasize a point or to indicate agreement or disagreement,but generally only when the information is related to a central theme or is clearly marked as important</li><li>make connections between the key ideas in a conversation,particularly if the ideas are related to a central theme or are repeated</li></ul>";
        } else if (score > 13 && score <=21){
            retDes = "<p>Test takers who receive a score at the <b>INTERMEDIATE</b> level typically understand conversations and lectures in English that present a wide range of listening demands.These demands can include di cult vocabulary(uncommon terms,or colloquial or gurative language),complex grammatical structures and/or abstract or complex ideas.However,lectures and conversations that require the listener to make sense of unexpected or seemingly contradictory information may present some di culty.</p><p>When listening to conversations and lectures like these,test takers at the<b>INTERMEDIATE</b>level typically can:</p><ul><li>understandexplicitlystatedmainideasandimportantdetails,especiallyifthey are reinforced,but may have di culty understanding main ideas that must be inferred or important details that are not reinforced</li><li>understand how information is being used(for example,to provide support or describe a step in a complex process)</li><li>recognize how pieces of information are connected(for example,in a cause-and-e ect relationship)</li><li>understand,thoughperhapsnotconsistently,waysthatspeakersuselanguage for purposes other than to give information(for example,to emphasize a point,express agreement or disagreement,or convey intentions indirectly)</li><li>synthesize information from adjacent parts of a lecture or conversation and make correct inferences on the basis of that information,but may have di culty synthesizing information from separate parts of a lecture or conversation</li></ul>";
        } else if (score > 21){
            retDes = "<p>Test takers who receive a score at the <b>HIGH</b> level typically understand conversations and lectures in English that present a wide range of listening demands.These demands can include di cult vocabulary(uncommon terms,or colloquial or gurative language),complex grammatical structures,abstract or complex ideas,and/or making sense of unexpected or seemingly contradictory information.</p><p>When listening to lectures and conversations like these,test takers at the<b>HIGH</b>level typically can:</p><ul><li>understand main ideas and important details,whether they are stated or implied</li><li>distinguish more important ideas from less important ones</li><li>understand how information is being used(for example,to provide evidence for a claim or describe a step in a complex process)</li><li>recognize how pieces of information are connected(for example,in a cause-and-e ect relationship)</li><li>understand many di erent ways that speakers use language for purposes other than to give information(for example,to emphasize a point,express agreement or disagreement,or convey intentions indirectly)</li><li>synthesize information,even when it is not presented in sequence,and make correct inferences on the basis of that information</li></ul>";
        }
        return retDes;
    }

    private String getSpeakingScoreEvaluate(int score){
        String ret = "";
        if (score <= 2){
            ret = "WEAK(0-1)";
        } else if (score > 2 && score <=4){
            ret = "LIMITED(1.5-2)";
        } else if (score > 4 && score <= 6){
            ret = "FAIR(2.5-3)";
        } else if (score > 6){
            ret = "GOOD(3.5-4)";
        }
        return ret;
    }

    private String getSpeakingEvaluateDes4FamilySkill(int score){
        String retDes = "";
        if (score <= 2){
            retDes = "<p>Your responses are incomplete.They contain little or no content and are difficult for listeners to understand.</p>";
        } else if (score > 2 && score <=4){
            retDes = "<p>Your responses indicate some difficulty speaking in English about everyday experiences and opinions.Listeners sometimes have trouble understanding you because of noticeable problems with pronunciation,grammar and vocabulary.While you are able to respond partially to the questions,you are not able to fully develop your ideas,possibly due to limited vocabulary and grammar.</p>";
        } else if (score > 4 && score <= 6){
            retDes = "<p>Your responses indicate you are able to speak in English about your personal experiences and opinions in a mostly clear and coherent manner.Your speech is mostly clear with only occasional errors.Grammar and vocabulary are somewhat limited and include some errors.</p>";
        } else if (score > 6){
            retDes = "<p>Your responses indicate an ability to communicate your personal experiences and opinions e ectively in English.Overall,your speech is clear and uent.Your use of vocabulary and grammar is e ective with only minor errors.Your ideas are generally well developed and expressed coherently.</p>";
        }
        return retDes;
    }

    private String getSpeakingEvaluateDes4CampusSkill(int score){
        String retDes = "";
        if (score <= 2){
            retDes = "<p>Your responses are incomplete.They include little or no information about the topic.Your speech is often difficult for listeners to understand,and the meaning is unclear.</p>";
        } else if (score > 2 && score <=4){
            retDes = "<p>Your responses indicate that you have some difficulty speaking in English about information from conversations,newspaper articles,university publications and so on.While you are able to talk about some of the key information from these sources,limited grammar and vocabulary may prevent you from fully expressing your ideas.Problems with pronunciation make it difficult for listeners to understand you at times.</p>";
        } else if (score > 4 && score <= 6){
            retDes = "<p>Your responses indicate you are able to speak in English about your personal experiences and opinions in a mostly clear and coherent manner.Your speech is mostly clear with only occasional errors.Grammar and vocabulary are somewhat limited and include some errors.</p>";
        } else if (score > 6){
            retDes = "<p>Your responses demonstrate an ability to speak in English about reading material and experiences typically encountered by university students.You are able to convey relevant information about conversations,newspaper articles and campus bulletins;however,some details are missing or inaccurate.Limitations of grammar,vocabulary and pronunciation at times cause difficulty for the listener.However,they do not seriously interfere with overall communication.</p>";
        }
        return retDes;
    }

    private String getSpeakingEvaluateDes4AcademicSkill(int score){
        String retDes = "";
        if (score <= 2){
            retDes = "<p>Your responses are incomplete.They include little or no information about the topic.Your speech is often difficult for listeners to understand,and the meaning is unclear.</p>";
        } else if (score > 2 && score <=4){
            retDes = "<p>In your responses,you are able to use English to talk about the basic ideas from academic reading or lecture materials,but,in general,you include few relevant or accurate details.It is sometimes difficult for listeners to understand your responses because of problems with grammar,vocabulary and pronunciation.Overall,you are able to respond in a general way to the questions,but the amount of information in your responses is limited and the expression of ideas is often vague and unclear.</p>";
        } else if (score > 4 && score <= 6){
            retDes = "<p>Your responses demonstrate that you are able to speak in English about academic reading and lecture material,with only minor communication problems.For the most part,your speech is clear and easy to understand.However,some problems with pronunciation and intonation may occasionally cause difficulty for the listener.Your use of grammar and vocabulary is adequate to talk about the topics,but some ideas are not fully developed or are inaccurate.</p>";
        } else if (score > 6){
            retDes = "<p>Your responses demonstrate an ability to communicate effectively in English about academic topics typical of first-year university studies.Your speech is mostly clear and fluent.You are able to use appropriate vocabulary and grammar to explain concepts and ideas from reading or lecture material.You are able to talk about key information and relevant details with only minor inaccuracies.</p>";
        }
        return retDes;
    }

    private String getWritingScoreEvaluate(float score){
        String ret = "";
        if (score == 0){
            ret = "ZERO(0)";
        } else if (score > 0 && score <=2){
            ret = "LIMITED(1-2)";
        } else if (score > 2 && score <= 4){
            ret = "FAIR(2.5-3.5)";
        } else if (score > 4){
            ret = "GOOD(4-5)";
        }
        return ret;
    }

    private String getWritingEvaluateDes4ReadingSkill(float score){
        String retDes = "";
        if (score == 0){
            retDes = "";
        } else if (score > 0 && score <=2){
            retDes = "<p>Your response was judged as limited due to:</p><ul><li>failure to understand the lecture or reading passage</li><li>deficiencies in relating the lecture to the reading passage</li><li>many grammatical errors and/or very unclear expressions and sentence structures</li></ul>";
        } else if (score > 2 && score <= 4){
            retDes = "<p>You responded to the task,relating the lecture to the reading,but your response indicates weaknesses,such as:</p><ul><li>an important idea or ideas may be missing,unclear or inaccurate</li><li>it may not be clear how the lecture and the reading passage are related</li><li>grammatical mistakes or vague/incorrect uses of words may make the writing difficult to understand</li></ul>";
        } else if (score > 4){
            retDes = "<p>You responded well to the task,relating the lecture to the reading.Weaknesses,if you have any,might have to do with:</p><ul><li>slight imprecision in your summary of some of the main points</li><li>use of English that is occasionally ungrammatical or unclear</li></ul>";
        }
        return retDes;
    }

    private String getWritingEvaluateDes4KnowledgeSkill(float score){
        String retDes = "";
        if (score == 0){
            retDes = "";
        } else if (score > 0 && score <=2){
            retDes = "<p>You attempted to express your opinion,but your response indicates notable deficiencies,such as:</p><ul><li>Your response contains insufficient detail.</li><li>Your ideas and your connections of ideas are difficult to understand because of many grammatical errors and/or very unclear expressions and sentence structure.</li><li>Your response is only marginally related to the question that was asked.</li></ul>";
        } else if (score > 2 && score <= 4){
            retDes = "<p>You expressed ideas with reasons,examples and details,but your response indicated weaknesses,such as:</p><ul><li>You may not provide enough specific support and development for your main points.</li><li>Your ideas may be difficult to follow because of how you organize your essay or because of the language you use to connect your ideas.</li><li>Grammatical mistakes or vague/incorrect uses of words may make the writing difficult to understand.</li></ul>";
        } else if (score > 4){
            retDes = "<p>You responded with a well-organized and developed essay.Weaknesses,if you have any,might have to do with:</p><ul><li>use of English that is occasionally ungrammatical,unclear or unidiomatic</li><li>elaboration of ideas or connection of ideas that could have been stronger</li></ul>";
        }
        return retDes;
    }

}
